package com.jin.webvideo.base.linkedfile.io;

import java.io.IOException;
import java.io.InputStream;

public class CacheInputStream extends InputStream {
    // 缓冲区的指针位置
    private int position = 0;
    // 缓冲区的有效长度
    private int length = 0;
    // 强制填满缓冲区
    private boolean forcedFull;
    // 缓冲区
    private byte[] cache;
    // 输入流是否结束
    private boolean isEnd = false;
    // 输入流是否关闭
    private volatile boolean closed = false;
    private InputStream inputStream;

    public CacheInputStream(InputStream inputStream, int cacheSize, boolean forcedFull) {
        this.inputStream = inputStream;
        cache = new byte[cacheSize];
        this.forcedFull = forcedFull;
    }

    public CacheInputStream(InputStream inputStream) {
        // 缓存32KB
        this(inputStream, 1024*32, true);
    }

    @Override
    public long skip(long n) throws IOException {
        ensureOpen();
        // 缓冲区中的未读大小
        int remaining = this.remaining();
        if ( n >  remaining) {
            n = n - remaining;
            // 设置缓冲区已结束
            this.position = this.length;
            inputStream.skip(n);
            return n;
        }
        this.position += n;
        return n;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        ensureOpen();
        // 输入流结束且缓冲区已读完
        if (isEnd && cacheEnd()) {
            return -1;
        }
        // 检查缓冲区数据
        chkCache();
        // 冲缓冲区读
        return readFromCache(b, off, len);
    }

    @Override
    public int read() throws IOException {
        ensureOpen();
        // 输入流结束且缓冲区已读完
        if (isEnd && cacheEnd()) {
            return -1;
        }
        // 检查缓冲区数据
        chkCache();
        return readFromCache();
    }

    @Override
    public void close() throws IOException {
        ensureOpen();
        this.inputStream.close();
        this.closed = true;
    }

    private void ensureOpen() throws IOException {
        if (closed) {
            throw new IOException("Stream closed");
        }
    }
    // 冲缓冲区读
    private int readFromCache() {
        return this.cache[this.position++];
    }

    // 冲缓冲区读
    private int readFromCache(byte[] b, int off, int len) throws IOException {
        int remaining = this.remaining();
        int needCopy = Math.min(remaining, len);
        System.arraycopy(cache, this.position, b, off, needCopy);
        this.position += needCopy;
        return needCopy;
    }

    // 检查缓冲区是否读完，读完则继续缓存
    private void chkCache() throws IOException {
        if (this.forcedFull) {
            forceFullCache();
        } else {
            cache();
        }
    }

    // 强制填充满， 与普通缓存的区别是，当存在数据未到达时会阻塞，直到缓冲区满或所有数据到达
    private void forceFullCache() throws IOException {
        if (!cacheEnd()){
            return;
        }
        int needCache = cache.length;
        while (needCache > 0) {
            int size = inputStream.read(cache, cache.length - needCache, needCache);
            if (size == -1) {
                this.isEnd = true;
                break;
            }
            needCache -= size;
        }
        this.length = cache.length - needCache;
        this.position = 0;
    }

    protected void position(int position) {
        if (position > this.length) {
            position = this.length;
        }
        this.position = position;
    }

    protected int position() {
        return this.position;
    }

    // 普通缓存
    /*private void cache() throws IOException {
        while (cacheEnd() ) {
            int size = inputStream.read(cache, 0, cache.length);
            if (size == -1) {
                this.isEnd = true;
                return;
            }
            this.length = size;
            this.position = 0;
        }
    }*/

    private void cache() throws IOException {
        while (cacheEnd()) {
            // 当前缓冲区已满，清空缓冲区
            if (this.length == this.cache.length) {
                this.position = 0;
                this.length = 0;
            }
            // 缓冲区未满，写入缓冲区
            int size = inputStream.read(cache, this.position, this.cache.length - this.position);
            if (size == -1) {
                this.isEnd = true;
                return;
            }
            // 设置缓冲区有效长度
            this.length += size;
        }
    }


    // 缓冲区是否结束
    private boolean cacheEnd() {
        return this.remaining() <= 0;
    }
    // 返回缓冲区中未读的大小
    private int remaining() {
        return this.length - this.position;
    }
}
